winbrew_app\operations\repair/
resolution.rs1use anyhow::{Context, Result};
2
3use crate::AppContext;
4use crate::catalog;
5use crate::database;
6use crate::engines::{self, EngineKind};
7use crate::models::catalog::{CatalogInstaller, CatalogPackage};
8use crate::models::domains::installed::InstalledPackage;
9use crate::models::domains::package::{PackageId, PackageRef};
10use crate::operations::install::{self, InstallObserver};
11use crate::operations::remove;
12
13#[derive(Debug, Clone)]
14pub struct ResolvedFileRestoreTarget {
15 pub package: CatalogPackage,
16 pub installer: CatalogInstaller,
17 pub engine: EngineKind,
18 pub installed_package: InstalledPackage,
19}
20
21#[derive(Debug, Clone)]
22pub struct FileRestoreReinstallTarget {
23 pub catalog_package: CatalogPackage,
24 pub installed_version: String,
25}
26
27#[derive(Debug, Clone)]
28pub enum FileRestoreResolution {
29 Restore(Box<ResolvedFileRestoreTarget>),
30 Reinstall(Box<FileRestoreReinstallTarget>),
31}
32
33pub fn resolve_repair_catalog_package<FChoose>(
35 package_name: &str,
36 choose_package: FChoose,
37) -> Result<CatalogPackage>
38where
39 FChoose: FnMut(&str, &[CatalogPackage]) -> Result<usize>,
40{
41 let catalog_conn = crate::database::get_catalog_conn()?;
42 resolve_repair_catalog_package_with_conn(&catalog_conn, package_name, choose_package)
43}
44
45fn resolve_repair_catalog_package_with_conn<FChoose>(
46 catalog_conn: &crate::database::DbConnection,
47 package_name: &str,
48 choose_package: FChoose,
49) -> Result<CatalogPackage>
50where
51 FChoose: FnMut(&str, &[CatalogPackage]) -> Result<usize>,
52{
53 let package_ref = PackageRef::parse(package_name)
54 .with_context(|| format!("failed to parse package reference '{package_name}'"))?;
55
56 catalog::resolve_catalog_package_ref(catalog_conn, &package_ref, choose_package)
57}
58
59pub fn resolve_file_restore_target<FChoose>(
61 package_name: &str,
62 choose_package: FChoose,
63) -> Result<FileRestoreResolution>
64where
65 FChoose: FnMut(&str, &[CatalogPackage]) -> Result<usize>,
66{
67 let catalog_conn = crate::database::get_catalog_conn()?;
68 let conn = database::get_conn()?;
69 let package =
70 resolve_repair_catalog_package_with_conn(&catalog_conn, package_name, choose_package)?;
71 let installed_package = database::get_package(&conn, package_name)?
72 .with_context(|| format!("package '{package_name}' is not installed"))?;
73
74 if installed_package.version != package.version.to_string() {
75 return Ok(FileRestoreResolution::Reinstall(Box::new(
76 FileRestoreReinstallTarget {
77 catalog_package: package,
78 installed_version: installed_package.version,
79 },
80 )));
81 }
82
83 let installers = crate::database::get_installers(&catalog_conn, &package.id)?;
84 let selection_context = crate::catalog::SelectionContext::new(
85 crate::windows::host::host_profile(),
86 crate::windows::host::is_elevated(),
87 );
88 let installer = install::types::select_installer(&installers, selection_context)?;
89 let engine = engines::resolve_engine_for_installer(&installer)?;
90
91 if engine_requires_reinstall_only(engine) {
92 return Ok(FileRestoreResolution::Reinstall(Box::new(
93 FileRestoreReinstallTarget {
94 catalog_package: package,
95 installed_version: installed_package.version,
96 },
97 )));
98 }
99
100 Ok(FileRestoreResolution::Restore(Box::new(
101 ResolvedFileRestoreTarget {
102 package,
103 installer,
104 engine,
105 installed_package,
106 },
107 )))
108}
109
110pub(crate) fn engine_requires_reinstall_only(engine: EngineKind) -> bool {
111 matches!(engine, EngineKind::Font)
112}
113
114pub fn reinstall_package<O: InstallObserver>(
116 ctx: &AppContext,
117 catalog_package: &CatalogPackage,
118 observer: &mut O,
119) -> Result<install::InstallOutcome> {
120 let conn = database::get_conn()?;
121
122 if database::get_package(&conn, &catalog_package.name)?.is_some() {
123 remove::remove(&catalog_package.name, true).with_context(|| {
124 format!(
125 "failed to remove package before repair: {}",
126 catalog_package.name
127 )
128 })?;
129 }
130
131 let package_ref = PackageRef::ById(
132 PackageId::parse(catalog_package.id.as_str())
133 .with_context(|| format!("failed to parse catalog id '{}'", catalog_package.id))?,
134 );
135
136 install::run(ctx, package_ref, false, observer)
137 .with_context(|| format!("failed to reinstall package '{}'", catalog_package.name))
138}
139
140#[cfg(test)]
141mod tests {
142 use super::engine_requires_reinstall_only;
143 use crate::engines::EngineKind;
144
145 #[test]
146 fn engine_requires_reinstall_only_returns_true_for_fonts() {
147 assert!(engine_requires_reinstall_only(EngineKind::Font));
148 assert!(!engine_requires_reinstall_only(EngineKind::NativeExe));
149 assert!(!engine_requires_reinstall_only(EngineKind::Portable));
150 }
151}